﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using TagPropagator.Enums;
using Microsoft.Win32;
using System.Drawing;
using System.Windows.Media.Imaging;
using TagPropagator.Controls;
using TagPropagator.Views;

namespace TagPropagator.ViewModels
{
    public class GraphBuilderViewModel : INotifyPropertyChanged
    {
        //TODO:REFAKTOR
        public GraphControl TEST { get; set; }

        #region FIELDS

        private GraphBuilder _graphBuilder;
        private FeatureType _featureExtractor;
        private PropagationAgentType _propagationAgent;
        private PurgeAgentType _purgeAgent;
        private int _iterations;
        private Graph _graph;
        private double _similarityLimit;
        private bool _createEmpty;

        #endregion

        #region PROPERTIES

        public FeatureType FeatureExtractor
        {
            get { return _featureExtractor; }
            set { _featureExtractor = value; OnPropertyChanged("FeatureExtractor"); }
        }

        public PropagationAgentType PropagationAgent
        {
            get { return _propagationAgent; }
            set { _propagationAgent = value; OnPropertyChanged("PropagationAgent"); }
        }

        public PurgeAgentType PurgeAgent
        {
            get { return _purgeAgent; }
            set { _purgeAgent = value; OnPropertyChanged("PurgeAgent"); }
        }

        public int Iterations
        {
            get { return _iterations; }
            set { _iterations = value; OnPropertyChanged("Iterations"); }
        }

        public Graph CurrentGraph
        {
            get { return _graph; }
            set { _graph = value; OnPropertyChanged("CurrentGraph"); }
        }

        public double SimilarityLimit
        {
            get { return _similarityLimit; }
            set { _similarityLimit = value; OnPropertyChanged("SimilarityLimit"); }
        }

        public bool CreateEmpty
        {
            get { return _createEmpty; }
            set 
            {
                _createEmpty = value;
                if (value == true)
                    CurrentGraph = new Graph();
                else
                    CurrentGraph = null;
                OnPropertyChanged("CreateEmpty"); 
            }
        }

        //Commands
        public ICommand OpenGraphFileDialogCommand { get; private set; }
        public ICommand ConfirmOptionChangesCommand { get; private set; }
        public ICommand OpenImageFileDialogCommand { get; private set; }
        public ICommand BeginLearnCommand { get; private set; }
        public ICommand DrawGraphCommand { get; private set; }
        public ICommand SaveGraphCommand { get; private set; }
        public ICommand ShowNodeTagDialogCommand { get; private set; }

        //End of Commands
        #endregion

        #region INOTIFY PROPERTY CHANGED SECTION

        public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };

        public void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
        }

        #endregion
        
        /// <summary>
        /// Konstruktor
        /// </summary>
        public GraphBuilderViewModel()
        {
            //_graphBuilder = new GraphBuilder();
            InitializeCommands();
            _graphBuilder = new GraphBuilder(new Comparer(), new Serializer());
        }

        private void InitializeCommands()
        {
            OpenGraphFileDialogCommand = new RelayCommand(x => Commands.OpenGraphFileDialog(this), x => Commands.CanOpenGraphFileDialog(this));
            ConfirmOptionChangesCommand = new RelayCommand(x => Commands.ConfirmOptionChanges(this), x => Commands.CanConfirmOptionChanges(this));
            OpenImageFileDialogCommand = new RelayCommand(x => Commands.OpenImageFileDialog(this), x => Commands.CanOpenImageFileDialog(this));
            BeginLearnCommand = new RelayCommand(x => Commands.BeginLearn(this), x => Commands.CanBeginLearn(this));
            DrawGraphCommand = new RelayCommand(x => Commands.DrawGraph(this), x => Commands.CanBeginLearn(this));
            SaveGraphCommand = new RelayCommand(x => Commands.SaveGraph(this), x => Commands.CanBeginLearn(this));
            ShowNodeTagDialogCommand = new RelayCommand(x => Commands.ShowNodeTagDialog(this), x => Commands.CanBeginLearn(this));
            
        }

        public static class Commands
        {
            /// <summary>
            /// Akcja otwierająca okno dialogowe do wyboru pliku zawierającego zserializowany Graf.
            /// </summary>
            /// <param name="Sender">ViewModel którego dotyczy komenda</param>
            public static void OpenGraphFileDialog(GraphBuilderViewModel Sender)
            {
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.Filter = "xml files (*.xml)|*.xml";
                dialog.Multiselect = false;
                dialog.ShowDialog();
                if (!string.IsNullOrEmpty(dialog.FileName))
                {
                    try
                    {
                        Serializer serializer = new Serializer();
                        Sender.CurrentGraph = serializer.Deserialize(dialog.FileName);
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.ToString());
                        Sender.CurrentGraph = null;
                    }
                }
            }

            public static bool CanOpenGraphFileDialog(GraphBuilderViewModel Sender)
            {
                return !Sender.CreateEmpty;
            }

            /// <summary>
            /// Akcja potwierdzająca zmiany właściwości Graph Buildera.
            /// </summary>
            /// <param name="Sender"></param>
            public static void ConfirmOptionChanges(GraphBuilderViewModel Sender)
            {
                //TODO: dopisać tworzenie odpowiednich obiektów w zależności od enumeracji
                //dla FeatureExtractora, PurgeAgenta i PropagationAgenta
                GraphBuilder builder = Sender._graphBuilder;
                builder.Graph = Sender.CurrentGraph;
                switch (Sender.PropagationAgent)
                {
                    case PropagationAgentType.DFSAgent:
                        builder.PropagationAgent = new DFSAgent();
                        break;
                }
                switch (Sender.PurgeAgent)
                {
                    case PurgeAgentType.PurgeAgent:
                        builder.PurgeAgent = new PurgeAgent();
                        break;
                }
                switch (Sender.FeatureExtractor)
                {
                    case FeatureType.DOUBLE:
                        builder.FeatureExtractor = new FeatureExtractor();
                        break;
                    default:
                        builder.FeatureExtractor = new FeatureExtractor();
                        break;
                }
                //REFAKTOR - umiescic ekstrakcje cech w stosownym miejscu
                foreach (Node node in builder.Graph.Nodes)
                {
                    using (Image i = node.ImageFile)
                    {
                        node.Features = builder.FeatureExtractor.ExtractFeatures(i);
                    }
                }

                //nie wiem czy to dobre miejsce
                //Sender.TEST.Graph = Sender._graphBuilder.Graph;
            }

            /// <summary>
            /// Określa czy komenda ConfirmOptionChanges może zostać wywołana
            /// </summary>
            /// <param name="Sender"></param>
            /// <returns></returns>
            public static bool CanConfirmOptionChanges(GraphBuilderViewModel Sender)
            {
                if (Sender.CurrentGraph == null)
                    return false;
                if (Sender.Iterations <= 0)
                    return false;
                if (Sender.SimilarityLimit < 0 && Sender.SimilarityLimit >= 1)
                    return false;
                return true;
            }

            public static void OpenImageFileDialog(GraphBuilderViewModel Sender)
            {
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.Multiselect = true;
                dialog.Filter = "Image files(*jpg;*gif;*png)|*.jpg;*gif;*png";
                dialog.ShowDialog();
                if (dialog.FileNames.Length > 0)
                {
                    try
                    {
                        foreach (string fileName in dialog.FileNames)
                        {
                            Node node = new Node();
                            node.ImageFile = Image.FromFile(fileName);
                            node.Image = fileName;
                            Sender._graphBuilder.AddNode(node);
                            //Sender.TEST.DrawNode(node);
                        }
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show(e.ToString());
                    }
                }
            }

            public static bool CanOpenImageFileDialog(GraphBuilderViewModel Sender)
            {
                if (Sender._graphBuilder == null || Sender._graphBuilder.Graph == null)
                    return false;
                return true;
            }

            public static void BeginLearn(GraphBuilderViewModel Sender)
            {
                Sender._graphBuilder.PurgeAgent.Purge(Sender._graphBuilder.Graph, Sender.SimilarityLimit);

                Sender._graphBuilder.PropagateKnowledge(Sender.Iterations);
            }

            public static bool CanBeginLearn(GraphBuilderViewModel Sender)
            {
                if (Sender._graphBuilder == null || Sender._graphBuilder.Graph == null)
                    return false;
                if (Sender.SimilarityLimit < 0 && Sender.SimilarityLimit >= 1)
                    return false;
                return true;
            }

            public static void DrawGraph(GraphBuilderViewModel Sender)
            {
                Sender._graphBuilder.Purge(Sender.SimilarityLimit);
                Sender.TEST.Graph = Sender._graphBuilder.Graph;
            }

            public static void SaveGraph(GraphBuilderViewModel Sender)
            {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "xml files (*.xml)|*.xml";
                dialog.ShowDialog();
                if (!string.IsNullOrEmpty(dialog.FileName))
                {
                    try
                    {
                        Sender._graphBuilder.Save(dialog.FileName);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }
                }
            }

            public static void ShowNodeTagDialog(GraphBuilderViewModel Sender)
            {
                AddTagWindow window = new AddTagWindow();
                AddTagViewModel model = new AddTagViewModel();
                window.DataContext = model;
                window.Show();
            }
        }
    }
}
